/**************************************************
|	File:		Game.cpp
|	Author:		Benjamin Budian
|	Course:		SGD
|	Purpose:	CGame class initializes SGD wrappers
|				& handles the game state machine
**************************************************/


#include "Game.h"

#include "../SGD Wrappers/CSGD_Direct3D.h"
#include "../SGD Wrappers/CSGD_DirectInput.h"
#include "../SGD Wrappers/CSGD_TextureManager.h"
#include "../SGD Wrappers/CSGD_XAudio2.h"

#include "IGameState.h"
#include "MainMenuState.h"
#include "Player.h"

#include <cassert>
#include <ctime>

#define FULLSCREEN_WIDTH 1024
#define FULLSCREEN_HEIGHT 1024
#define FULLSCREEN_HEIGHT_PROJECTOR 1024
#define FULLSCREEN_WIDTH_PROJECTOR 768

#define WINDOWED_WIDTH 1024
#define WINDOWED_HEIGHT 1024


/*************************************************/
// SINGLETON:

// Instantiate the static member
/*static*/ CGame* CGame::s_pInstance = nullptr;

// GetInstance
//	- returns the ONE singleton object
/*static*/ CGame* CGame::GetInstance( void )
{
	// Allocate the static pointer if necessary
	if( s_pInstance == nullptr )
		s_pInstance = new CGame;

	// Return the singleton
	return s_pInstance;
}

// DeleteInstance
//	- deallocate the singleton object
/*static*/ void CGame::DeleteInstance( void )
{
	delete s_pInstance;
	s_pInstance = nullptr;
}


/*************************************************/
// Constructor
//	- set all the data members to 'clean' values
CGame::CGame(void)
{
	m_pD3D			= nullptr;
	m_pDI			= nullptr;
	m_pTM			= nullptr;
	m_pXA			= nullptr;

	m_pFont			= nullptr;


	m_nScreenWidth	= 1;
	m_nScreenHeight	= 1;
	m_bIsWindowed	= true;

	m_pCurrState	= nullptr;

	m_dwCurrTime	= 0;

	
	
	// seed the random number generator
	srand( (unsigned int)time( nullptr ) );
}

/*************************************************/
// Destructor
CGame::~CGame(void)
{
}


/*************************************************/
// Initialize
//	- setup the SGD wrappers
void CGame::Initialize( HWND hWnd, HINSTANCE hInstance,
					int nWidth, int nHeight,
					bool bIsWindowed )
{
	// Store the SGD Wrapper singletons
	m_pD3D			= CSGD_Direct3D::GetInstance();
	m_pDI			= CSGD_DirectInput::GetInstance();
	m_pTM			= CSGD_TextureManager::GetInstance();
	m_pXA			= CSGD_XAudio2::GetInstance();

	// Store the parameters
	m_nScreenWidth	= nWidth;
	m_nScreenHeight	= nHeight;
	m_bIsWindowed	= bIsWindowed;


	// Initialize the wrappers
	m_pD3D->Initialize( hWnd, m_nScreenWidth, m_nScreenHeight, m_bIsWindowed, false );
	m_pDI->Initialize( hWnd, hInstance, DI_KEYBOARD /*| DI_MOUSE | DI_JOYSTICKS*/ );
	m_pTM->Initialize( m_pD3D->GetDirect3DDevice(),	m_pD3D->GetSprite() );
	m_pXA->Initialize();


	// Initialize the Font
	m_pFont = new CBitmapFont;
	m_pFont->Initialize();

	// Start in the Main Menu state
	ChangeState( CMainMenuState::GetInstance() );

	// Store the current time
	m_dwCurrTime	= GetTickCount();

		

}

/*************************************************/
// Main
//	- run one frame of the gameplay
bool CGame::Main( void )
{
	// Check input
	if( Input() == false )
		return false;

	// Update & render
	Update();
	Render();
	return true;
}

/*************************************************/
// Terminate
//	- shutdown the SGD wrappers (in REVERSE ORDER)
void CGame::Terminate( void )
{
	// Exit the current state
	ChangeState( nullptr );

	
	// Shutdown the font
	if( m_pFont != nullptr )
	{
		m_pFont->Shutdown();
		delete m_pFont;
		m_pFont = nullptr;
	}
		

	// Shutdown the wrappers
	m_pXA->Terminate();
	m_pTM->Terminate();
	m_pDI->Terminate();
	m_pD3D->Terminate();

	
	// Reset the data members to 'clean' values
	m_pD3D			= nullptr;
	m_pDI			= nullptr;
	m_pTM			= nullptr;
	m_pXA			= nullptr;
	
	m_nScreenWidth	= 1;
	m_nScreenHeight	= 1;
	m_bIsWindowed	= true;
}


/*************************************************/
// Input
//	- handle user input
bool CGame::Input( void )
{
	// Read the devices
	m_pDI->ReadDevices();
	
	if( ((m_pDI->KeyDown(DIK_LALT) == true ) || (m_pDI->KeyDown(DIK_RALT) == true)) && m_pDI->KeyPressed(DIK_RETURN) == true){
		m_bIsWindowed = !m_bIsWindowed;

		if(m_bIsWindowed == true)
			CSGD_Direct3D::GetInstance()->Resize(WINDOWED_WIDTH,WINDOWED_HEIGHT,m_bIsWindowed);
		else
			CSGD_Direct3D::GetInstance()->Resize(FULLSCREEN_WIDTH,FULLSCREEN_HEIGHT,m_bIsWindowed);

		return true;
	}
	
	return m_pCurrState->Input();	// current state handles input
}

/*************************************************/
// Update
//	- update the game
void CGame::Update( void )
{
	// Update the audio
	m_pXA->Update();


	// Calculate the elapsed time between frames
	DWORD dwNow = GetTickCount();
	float fElapsedTime = (dwNow - m_dwCurrTime) / 1000.0f;
	m_dwCurrTime = dwNow;


	// Cap the elapsed time to 1/8th
	if( fElapsedTime > 0.125f )
		fElapsedTime = 0.125f;



	// Current state updates
	m_pCurrState->Update( fElapsedTime );
}

/*************************************************/
// Render
//	- render the game
void CGame::Render( void )
{
	// Clear the backbuffer
	m_pD3D->Clear( D3DCOLOR_XRGB( 50, 50, 50 ) );	// Dark gray

	// Begin rendering
	m_pD3D->DeviceBegin();
	m_pD3D->SpriteBegin();
	{
		// Current state renders
		m_pCurrState->Render();
	}
	m_pD3D->SpriteEnd();
	m_pD3D->DeviceEnd();

	// Present the backbuffer to the screen
	m_pD3D->Present();
}


/*************************************************/
// ChangeState
//	- switch between states
void CGame::ChangeState( IGameState* pState )
{
	// Exit the current state (if any)
	if( m_pCurrState != nullptr )
		m_pCurrState->Exit();


	// Store the new state
	m_pCurrState = pState;


	// Enter the new state (if any)
	if( m_pCurrState != nullptr )
		m_pCurrState->Enter();
}
